package hikari.instruments;

import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import hikari.gui.panels.BasicObjectPropertiesPanel;
import hikari.gui.panels.ModifyPanel;
import hikari.managers.EngineProperties;
import hikari.managers.Manager3D;
import hikari.managers.SelectionManager;
import hikari.objects3d.Basic3dObject;
import hikari.utils.Utils;
import hikari.utils.Vector;
import java.awt.Cursor;
import org.swig.jirr.ISceneCollisionManager;
import org.swig.jirr.ISceneNode;
import org.swig.jirr.SColor;
import org.swig.jirr.SMaterial;
import org.swig.jirr.vector2di;
import org.swig.jirr.vector3df;

/**
 * @author Dmitry
 */
public abstract class AbstractTransformationInstrument extends SelectInstrument {

    protected SMaterial xColor;
    protected SMaterial yColor;
    protected SMaterial zColor;
    protected SMaterial selectedColor;
    protected vector3df gizmo_position;
    // space - empty; 'x','y','z' - for axises
    protected char selectedAxis = ' ';
    protected boolean dragAxis = false;
    protected int pressedXPosition = 0;
    protected int pressedYPosition = 0;
    protected vector2di mouseNorth;
    protected vector2di mouseSouth;
    protected vector2di mouseWest;
    protected vector2di mouseEast;
    protected Vector axisDirection;
    protected ISceneNode currentTransformedObject;
    @Wire
    protected EngineProperties engineProperties;
    @Wire
    private Manager3D manager3D;
    @Wire
    private SelectionManager selectionManager;
    @Wire
    private ModifyPanel modifyPanel;
    @Wire
    protected Utils utils;

    protected abstract void drawGizmo(ISceneNode node);

    protected abstract float proportionalScaling(float length);

    protected abstract void changeObject(ISceneNode node, char axis, float length);

    protected abstract void getNodeStateBeforeChangeState(ISceneNode node);

    protected abstract char getIntersectedAxis(int x, int y);

    protected void onEndTransformation(ISceneNode node) {
    }

    protected AbstractTransformationInstrument() {
    }

    @PostWiring
    private void init() {
        xColor = new SMaterial().setEmissiveColor(SColor.RED).setZBuffer((short) 0);
        yColor = new SMaterial().setEmissiveColor(SColor.GREEN).setZBuffer((short) 0);
        zColor = new SMaterial().setEmissiveColor(SColor.BLUE).setZBuffer((short) 0);
        selectedColor = new SMaterial().setEmissiveColor(SColor.WHITE).setZBuffer((short) 0);
    }

    protected float getGizmoLength(vector3df position) {
        vector3df camPos = manager3D.getSceneManager().getActiveCamera().getAbsolutePosition();
        vector3df distance = position.subtractOperator(camPos);
        float length = distance.getLength();
        float axisSizes = engineProperties.getFloatProperty("AxisSize");
        if (length > 70) {
            length = length - 70;
            return axisSizes * (length / 90.f + 1);
        }
        return axisSizes;
    }

    @Override
    public void selectObject(Basic3dObject currentSelection, Basic3dObject newSelection) {
        if (newSelection == null) {
            gizmo_position = null;
        }
        super.selectObject(currentSelection, newSelection);
    }

    protected boolean intersectionLineWithMouseCross(vector2di start, vector2di end) {
        return Utils.lineHitTest(start, end, mouseNorth, mouseSouth)
                || Utils.lineHitTest(start, end, mouseWest, mouseEast);
    }

    @Override
    public void mouseDown(int x, int y, int button, int modifier) {
        currentTransformedObject = null;
        if (isLeftMouseDown(modifier) && !isRightMouseDown(modifier)) {
            Basic3dObject selected = selectionManager.getCurrentSelectedObject();
            if (selectedAxis != ' ' && selected != null) {
                ISceneCollisionManager cm = manager3D.getSceneManager().getSceneCollisionManager();
                pressedXPosition = x;
                pressedYPosition = y;
                vector2di axisStart = cm.getScreenCoordinatesFrom3DPosition(gizmo_position);
                vector3df axisMatrix = null;
                float axisSizes = engineProperties.getFloatProperty("AxisSize");
                switch (selectedAxis) {
                    case 'x':
                        axisMatrix = new vector3df(axisSizes, 0, 0);
                        break;
                    case 'y':
                        axisMatrix = new vector3df(0, axisSizes, 0);
                        break;
                    case 'z':
                        axisMatrix = new vector3df(0, 0, axisSizes);
                        break;
                }

                vector2di axisEnd = cm.getScreenCoordinatesFrom3DPosition(gizmo_position.addOperator(axisMatrix));
                axisDirection = new Vector(axisEnd.getX() - axisStart.getX(), axisEnd.getY() - axisStart.getY());
                axisDirection.length();
                currentTransformedObject = selected.getMeshSceneNode();
                currentTransformedObject.updateAbsolutePosition();
                getNodeStateBeforeChangeState(currentTransformedObject);
                dragAxis = true;
                return;
            }
        }

        super.mouseDown(x, y, button, modifier);
    }

    @Override
    public void mouseMove(int x, int y, int dx, int dy, int modifier) {
        if (dragAxis) {
            Vector mouseMove = new Vector(x - pressedXPosition, y - pressedYPosition);
            Vector projection = mouseMove.getVectorProjection(axisDirection);
            float length = (float) projection.length();
            Vector bigAxis = axisDirection.clone().scalarMultiple(5000.0);
            Vector subtraction = bigAxis.clone().subtract(projection);
            if (subtraction.length() > bigAxis.length()) {
                length *= -1;
            }

            length = proportionalScaling(length);
            changeObject(currentTransformedObject, selectedAxis, length);
            utils.setModified();
        } else if (gizmo_position != null) {
            mouseNorth = new vector2di(x, y - 3);
            mouseSouth = new vector2di(x, y + 3);
            mouseWest = new vector2di(x - 3, y);
            mouseEast = new vector2di(x + 3, y);
            selectedAxis = getIntersectedAxis(x, y);
        }

        super.mouseMove(x, y, dx, dy, modifier);
    }

    @Override
    public void onAfterRenderBeforeGui() {
        Basic3dObject object = selectionManager.getCurrentSelectedObject();
        if (object != null) {
            drawGizmo(object.getMeshSceneNode());
        }
        if (selectedAxis != ' ') {
            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        }

        super.onAfterRenderBeforeGui();
    }

    @Override
    public void mouseUp(int x, int y, int button, int modifier) {
        dragAxis = false;
        if (selectionManager.getCurrentSelectedObject() != null) {
            modifyPanel.updateTransformationData(BasicObjectPropertiesPanel.UPDATE_TYPE.ALL);
            if (currentTransformedObject != null) {
                onEndTransformation(selectionManager.getCurrentSelectedObject().getMeshSceneNode());
            }
        }
        currentTransformedObject = null;
        super.mouseUp(x, y, button, modifier);
    }
}
